//
// Created by hjie on 23-5-13.
//

#include "redis_message.h"
#include <sstream>

RedisMessage::RedisMessage(RedisMessageType type)
{
    m_type = type;
}

RedisMessage *RedisMessage::ParseRedisMessage(const uint8_t *data, int length, int & use_length, EmRedisParseStatus & status)
{
    RedisMessage * message = nullptr;
    uint8_t type = data[0];
    switch (type)
    {
        case '+':
        {
            message = new RedisMessageString();
            break;
        }
        case '$':
        {
            message = new RedisMessageLengthString();
            break;
        }
        case '*':
        {
            message = new RedisMessageArray();
            break;
        }
        case ':':
        {
            message = new RedisMessageNumber();
            break;
        }
        case '-':
        {
            message = new RedisMessageError();
            break;
        }
        default:
        {
            status = EmParseStatusError;
            break;
        }
    }
    if (message != nullptr)
    {
        use_length = 0;
        bool result = message->ParseMessage(data, length, use_length);
        if (!result)
        {
            delete message;
            message = nullptr;
        }
        else
        {
            status = EmParseStatusFinish;
        }
    }
    return message;
}

RedisMessage * RedisMessage::ParseCommand(const std::string &command_line)
{
    std::vector<std::string> command_vector;
    for (size_t index = 0; index < command_line.size(); index++)
    {
        if (command_line[index] == ' ' || command_line[index] == '\r' || command_line[index] == '\n' || command_line[index] == '\0')
        {
            continue;
        }
        std::string value;
        for (size_t i = index; i < command_line.size(); i++, index++)
        {
            if (command_line[i] == ' ')
            {
                break;
            }
            value += command_line[i];
        }
        if (!value.empty())
        {
            command_vector.push_back(value);
        }
    }
    RedisMessage * message = nullptr;
    if (!command_vector.empty())
    {
        message = new RedisMessageArray();
        for (auto & data : command_vector)
        {
            RedisMessageLengthString * msg = new RedisMessageLengthString();
            msg->Value(data);
            ((RedisMessageArray*)message)->AddMessage(msg);
        }
    }
    /*
    if (command_vector.size() == 1)
    {
        message = new RedisMessageLengthString();
        ((RedisMessageLengthString*)message)->Value(command_vector.at(0));
    }
    else if (command_vector.size() > 1)
    {
        message = new RedisMessageArray();
        for (auto & data : command_vector)
        {
            RedisMessageLengthString * msg = new RedisMessageLengthString();
            msg->Value(data);
            ((RedisMessageArray*)message)->AddMessage(msg);
        }
    }
    */
    else
    {
        message = nullptr;
    }
    return message;
}

//////////////////////////////////////////////////String Type///////////////////////////////////////////////////////////
RedisMessageString::RedisMessageString() : RedisMessage(EmStringType)
{
    (void)m_prefix;
}

bool RedisMessageString::ParseMessage(const uint8_t *data, int length, int & use_length)
{
    int index = 1;
    for (; index < length; index++)
    {
        if (data[index] == '\r')
        {
            index = index + 2;
            use_length = index;
            break;
        }
        m_value.push_back(data[index]);
    }
    if (use_length == 0)
    {
        return false;
    }
    return true;
}

////////////////////////////////////////////////Length String Type//////////////////////////////////////////////////////
RedisMessageLengthString::RedisMessageLengthString() : RedisMessage(EmLengthStringType)
{
    (void)m_prefix;
}

bool RedisMessageLengthString::ParseMessage(const uint8_t *data, int length, int &use_length)
{
    // $4\r\nABCD\r\n
    // $0\r\n\r\n
    // $-1\r\n
    int index = 1;
    int value_length = 0;
    int is_null = 0;
    for (; index < length; index++)
    {
        if (data[index] == '\r')
        {
            index = index + 2;
            use_length = index;
            break;
        }
        if (data[index] == '-' && data[index - 1] == m_prefix)
        {
            is_null = 1;
            index = index + 3;
            break;
        }
        value_length = (value_length * 10) + (data[index] - '0');
    }
    if (use_length != 0)
    {
        if (value_length != 0)
        {
            m_value.assign((const char *)data + index, value_length);
        }
        index = index + value_length + 2;
        use_length = index;
        return true;
    }
    if (is_null == 1)
    {
        use_length = index;
        return true;
    }
    return false;
}

bool RedisMessageLengthString::ComposeMessage(std::string &content)
{
    int size = m_value.size();
    content += m_prefix;
    content += std::to_string(size) + "\r\n";
    content += m_value + "\r\n";

    return true;
}

/////////////////////////////////////////////String Array type//////////////////////////////////////////////////////////
RedisMessageArray::RedisMessageArray() : RedisMessage(EmArrayStringType)
{
    (void)m_prefix;
    m_value_vec.clear();
}

RedisMessageArray::~RedisMessageArray()
{
    for (auto & data : m_value_vec)
    {
        delete data;
    }
}

bool RedisMessageArray::ParseMessage(const uint8_t *data, int length, int &use_length)
{
    int index = 1;
    int array_length = 0;
    int is_null = 0;
    (void)is_null;
    for (; index < length; index++)
    {
        if (data[index] == '\r')
        {
            index = index + 2;
            break;
        }
        if (data[index] == '-' && data[index - 1] == m_prefix)
        {
            is_null = 1;
            index = index + 4;
            break;
        }
        array_length = (array_length * 10) + (data[index] - '0');
    }
    for (int array_index = 0; array_index < array_length; array_index++)
    {
        EmRedisParseStatus status;
        int unit_msg_length = 0;
        RedisMessage * message = RedisMessage::ParseRedisMessage(data + index, length - index, unit_msg_length, status);
        if (message == nullptr)
        {
            // 数据不够
            return false;
        }
        index += unit_msg_length;
        m_value_vec.push_back(message);
    }
    use_length = index;
    return true;
}

bool RedisMessageArray::ComposeMessage(std::string &content)
{
    int array_size = m_value_vec.size();
    content += m_prefix;
    content += std::to_string(array_size) + "\r\n";
    for (auto & data : m_value_vec)
    {
        data->ComposeMessage(content);
    }
    return true;
}

/////////////////////////////////////////////Number type////////////////////////////////////////////////////////////////
RedisMessageNumber::RedisMessageNumber() : RedisMessage(EmNumberType)
{
    (void)m_prefix;
    m_number_value = 0;
}

bool RedisMessageNumber::ParseMessage(const uint8_t *data, int length, int &use_length)
{
    int index = 1;
    int number = 0;
    int is_zero_below = 0;
    for (; index < length; index++)
    {
        if (data[index] == '\r')
        {
            index = index + 2;
            use_length = index;
            break;
        }
        if (data[index] == '-')
        {
            is_zero_below = 1;
            continue;
        }
        number = (number * 10) + (data[index] - '0');
    }
    if (use_length == 0)
    {
        return false;
    }
    m_number_value = number;
    if (is_zero_below)
    {
        m_number_value = -m_number_value;
    }
    return true;
}

bool RedisMessageNumber::ComposeMessage(std::string &content)
{
    content += m_prefix;
    content += std::to_string(m_number_value) + "\r\n";
    return true;
}

///////////////////////////////////////////Error message type///////////////////////////////////////////////////////////
RedisMessageError::RedisMessageError() : RedisMessage(EmErrorType)
{
    (void)m_prefix;
}

bool RedisMessageError::ParseMessage(const uint8_t *data, int length, int &use_length)
{
    int index = 1;
    for (; index < length; index++)
    {
        if (data[index] == '\r')
        {
            index = index + 2;
            use_length = index;
            break;
        }
    }
    if (use_length != 0)
    {
        m_value.assign((const char*)data + 1, index - 2);
        return true;
    }
    return false;
}

///////////////////////////////////////////internal message type////////////////////////////////////////////////////////
RedisInternalMessage::RedisInternalMessage() : RedisMessage(EmInternalType)
{

}

bool RedisInternalMessage::ParseMessage(const uint8_t *data, int length, int &use_length)
{
    return true;
}

bool RedisInternalMessage::ComposeMessage(std::string &content)
{
    return true;
}


